package org.papillion39.sechelper.application.business;

import java.security.Principal;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import javax.annotation.Resource;
import javax.annotation.security.DeclareRoles;
import javax.annotation.security.RolesAllowed;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

import org.papillion39.sechelper.application.business.model.User;
import org.papillion39.sechelper.application.data.model.Account;
import org.papillion39.sechelper.application.data.model.Account_;
import org.papillion39.sechelper.application.data.model.RoleGroup;

/**
 *
 * @author Jonathan Paz <jonathan.paz@ottr.com>
 */
@Stateless
@DeclareRoles({"user","secretary","distributor","admin"})
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public class UserManagement implements UserManagementLocal {
    @PersistenceContext
    private EntityManager em;

    @Resource
    private SessionContext context;

    // Add business logic below. (Right-click in editor and choose
    // "Insert Code > Add Business Method")

    @Override
    @RolesAllowed({"user","admin"})
    public User getUser(String username) {
        Principal principal = context.getCallerPrincipal();
        if (!context.isCallerInRole("admin") && !principal.getName().equals(username)) {
            throw new SecurityException("Non-admins can only manage themselves");
        }
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Account> cq = cb.createQuery(Account.class);
        Root<Account> account = cq.from(Account.class);
        cq.select(account).where(cb.equal(account.get(Account_.userName), username));
        TypedQuery<Account> q = em.createQuery(cq);
        Account a = q.getSingleResult();
        List<String> roles = new LinkedList<>();
        a.getRoleGroupSet().stream().forEach((r) -> {
            roles.add(r.getName());
        });
        return new User(a.getId(), a.getUserName(), roles);
    }

    @Override
    @RolesAllowed("admin")
    public List<User> getUsers() {
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Account> cq = cb.createQuery(Account.class);
        Root<Account> account = cq.from(Account.class);
        cq.select(account);
        TypedQuery<Account> q = em.createQuery(cq);
        List<Account> accounts = q.getResultList();
        List<User> users = new LinkedList<>();
        accounts.stream().forEach((a) -> {
            List<String> roles = new LinkedList<>();
            a.getRoleGroupSet().stream().forEach((r) -> {
                roles.add(r.getName());
            });
            users.add(new User(a.getId(), a.getUserName(), roles));
        });
        return users;
    }

    @Override
    public void saveUsers(List<User> users) {
        users.stream().forEach((user) -> {
            saveUser(user);
        });
    }

    @Override
    public void saveUser(User user) {
        Account acct = em.find(Account.class, user.getId());
        if (acct == null) {
            acct = new Account();
        }
        if (!Objects.equals(acct.getUserName(),user.getUsername())) {
            acct.setUserName(user.getUsername());
        }
        char[] pwd = user.getPassword();
        if (pwd != null && pwd.length > 0) {
            Utils.SaltedHash sh = Utils.generatePasswordHash(pwd);
            acct.setPassword(sh.hash);
            acct.setSalt(sh.salt);
            user.setPassword(null);
        }
        em.merge(acct);
    }

}
